<template>
  <div>
    <el-upload
        class="avatar-uploader"
        drag
        :auto-upload="false"
        :on-change="handleChange"
        :limit="1"
        v-loading="loading"
        ref="uploadRef">
      <el-avatar  v-if="imageUrl"  class="avatar"
                  :src="imageUrl"></el-avatar>
      <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
    </el-upload>
    <el-button round type="success" @click="uploadFileBtn" v-if="MD5Status" :loading-icon="Eleme" loading>
      解析资源中，请稍等...
    </el-button>
<!--    <el-button round v-loading="loading" type="success" @click="uploadFile" v-else>点击上传</el-button>-->
    <el-progress
        v-if="state.uploadProgress > 0"
        :percentage="state.uploadProgress"
        status="success">
    </el-progress>
  </div>
</template>

<script setup lang="ts">
import {reactive, ref, watchEffect} from 'vue';
import SparkMD5 from 'spark-md5';
import { checkMd5, merge, upload } from '@/api/fileupload.ts';
import { UploadProps } from 'element-plus';
import {Eleme, Plus} from '@element-plus/icons-vue';
const emit = defineEmits(['imgData','complete'])
const props = defineProps(['img','t'])
const imageUrl = ref('')
const loading=ref(false)
watchEffect(()=>{
  const t = props.t
  if (props.img){
    console.log(1,props.img);
    imageUrl.value = props.img
  }
})
const state = reactive({
  file: null as File | null,
  md5: '',
  uploadProgress: 0
});

interface fileBlob {
  start: number,
  end: number,
  index: number,
  hash: string,
  blob: Blob
}

const uploadRef = ref();
const MD5Status = ref(false);
const CHUNK_SIZE = 1024 * 1024 * 5; // 最少5M,minio合并最低要求
const THREAD_COUNT = navigator.hardwareConcurrency || 4;
const chunks = ref<fileBlob[]>([]);

const handleChange: UploadProps['onChange'] =async (uploadFile, uploadFiles) => {
  MD5Status.value = true;
  state.file = uploadFile.raw as File;
  await cutFile(state.file).then((res: any) => {
    chunks.value = res;
    const md5Values = res.map((it: any) => it.hash).join('');
    state.md5 = SparkMD5.hash(md5Values);
    MD5Status.value = false;
  });
  imageUrl.value = URL.createObjectURL(uploadFile.raw!)
  uploadFileBtn()
};

const uploadFileBtn = async () => {
  if (!state.file) return;
  loading.value=true
  state.uploadProgress = 0;

  const file = state.file;
  const md5 = state.md5;

  // 先询问后端哪些chunk已经上传过
  let uploadedChunks = new Set();
  await checkMd5(md5).then((response: any) => {
    uploadedChunks = new Set(response.msg.split(':').map(Number));
  });


  for (let i = 0; i < chunks.value.length; i++) {
    // 不包含片段
    if (!uploadedChunks.has(i + 1)) {
      await uploadChunk(chunks.value[i].blob, i + 1, md5);
    }
    state.uploadProgress = Math.floor(((i + 1) / chunks.value.length) * 100);
    if (state.uploadProgress === 100) {
      uploadRef.value.clearFiles();
    }
  }
  const formData = new FormData();
  formData.append('fileName', file.name);
  formData.append('md5', md5);
  // 通知后端进行合并
  await merge(formData).then(url => {
    console.log('文件url', url.data);
    imageUrl.value = url.data.substring(0,url.data.indexOf('?')).replace('http://124.221.209.30:19000/','https://img.springautumncome.asia/')
    emit('imgData', url.data.substring(0,url.data.indexOf('?')).replace('http://124.221.209.30:19000/','https://img.springautumncome.asia/'))
    emit('complete',true)
  });
  loading.value=false
};

const uploadChunk = async (chunk: Blob, chunkNumber: number, md5: string) => {
  const formData = new FormData();
  formData.append('chunk', chunk);
  formData.append('chunkNumber', chunkNumber.toString());
  formData.append('md5', md5);

  await upload(formData);
};


async function cutFile(file: File) {
  return new Promise((resolve, reject) => {
    const chunks = Math.ceil(file.size / CHUNK_SIZE);
    const threadChunkCount = Math.ceil(chunks / THREAD_COUNT);
    const result: any[] = [];
    let finishCount = 0;

    for (let i = 0; i < THREAD_COUNT; i++) {
      // 分配线程任务
      const worker = new Worker(new URL('@/utils/minio.ts', import.meta.url), {
        type: 'module'
      });
      const start = i * threadChunkCount;
      let end = (i + 1) * threadChunkCount;
      if (end > chunks) {
        end = chunks;
      }
      worker.postMessage({
        file,
        start,
        end,
        CHUNK_SIZE
      });
      worker.onmessage = e => {
        try {
          result[i] = e.data;
          worker.terminate();
          finishCount++;
          if (finishCount === THREAD_COUNT) {
            resolve(result.flat());
          }
        } catch (error) {
          reject(`Error processing the file in worker: ${error}`);
        }
      };
      worker.onerror = (error) => {
        reject(`Worker encountered an error: ${error}`);
      };
    }
  });
}
</script>
<style scoped>
.el-upload__tip{
  color: red;
}
.avatar-uploader .avatar {
  width: 178px;
  height: 178px;
  display: block;
}
.avatar-uploader .el-upload {
  border: 1px dashed var(--el-border-color);
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: var(--el-transition-duration-fast);
}

.avatar-uploader .el-upload:hover {
  border-color: var(--el-color-primary);
}

.el-icon.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  text-align: center;
}
</style>
/**基于`axios`封装请求
export const checkMd5 = (md5: string) => {
return http.get('/file/check/' + md5);
};
export function merge(data: any, config: any) {
return http.post('/file/merge', data, config);
}
export function upload(data: any, config: any) {
return http.post('/file/upload', data, config);
}*/